/*
 * @Author: wwssaabb
 * @Date: 2021-12-13 10:41:31
 * @LastEditTime: 2021-12-13 11:36:56
 * @FilePath: \handwritten-code\main\stringify.js
 */

/* 
  实现JSON.stringify
  类型如下转换：
    -- 基本类型
      undefined=>undefined
      null=>'null'
      boolean=>'true'/'false'
      string=>string
      number=>string(除了NaN、Infinity)
      NaN、Infinity=>'null'
      Symbol=>undefined
    -- 函数类型
      function=>undefined
    -- 对象（非函数）
      array=>会将元素中的undefined、null、symbol、function装换成字符null
      RegExp=>{}
      Date=>返回Date的toJSON字符串值
      普通对象=>
        - 如果有toJSON方法，则返回toJSON()的返回值
        - 如果属性值中出现了undefined、function、Symbol的值，会忽略这个属性
        - 以Symbol为键的属性会被忽略
      循环引用=>报错
*/

function jsonStringify(data) {
  let type = typeof data

  if (type !== 'object') {
    //有可能是 undefined、number、string、boolean、function、symbol

    //处理NaN、Infinity
    if (Number.isNaN(data) || data === Infinity) return 'null'
    //基本类型
    if (type === 'number' || type === 'string' || type === 'boolean') return '' + data
    //symbol类型、函数类型
    if (type === 'symbol' || type === 'undefined' || type === 'function') return undefined

  } else if (type === 'object') {
    //有可能是 object、Date、RegExp、Array、null

    //null
    if (data === null) return 'null'
    //date
    if (data instanceof Date) return data.toJSON()
    //RegExp
    if (data instanceof RegExp) return '{}'
    //array
    if (data instanceof Array) {
      //数组元素为function、Symbol、undefined、null时都为 'null'
      let res = []
      data.forEach(i => {
        let tp = typeof i
        if (tp === 'function' || tp === 'symbol' || tp === 'undefined' || i === null) {
          res.push('null')
        } else {
          res.push(jsonStringify(i))
        }
      })
      return res
    }
    //普通对象,symbol为key或function、undefined、symbol为值时忽略这个属性
    const keys = Object.keys(data)
    let res = keys.reduce((pre, cur) => {
      const value = data[cur]
      const tp = typeof value
      if (tp === 'function' || tp === 'symbol' || tp === 'undefined') return pre
      if (value === data) throw new Error('循环引用错误')
      pre[cur] = data[cur]
      return pre
    }, {})
  }
}